#include "LCommon.h"

initSing(RealmHandler);

void RealmHandler::Loop()
{
	m_listensocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
	SOCKADDR_IN sin;
	memset(&sin, NULL, sizeof(SOCKADDR_IN));
	sin.sin_port = htons(5000);
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_family = AF_INET;
	bind(m_listensocket, (const sockaddr*)&sin, sizeof(SOCKADDR_IN));
	listen(m_listensocket, 10);
	while(ThreadState)
	{
		FD_ZERO(&fset);
		m_clientlock.Acquire();
		FD_SET(m_listensocket, &fset);
		if(m_clients.size())
		{
			vector<RealmClient*>::iterator itr = m_clients.begin();
			vector<RealmClient*>::iterator end = m_clients.end();
			for( ; itr != end; ++itr)
			{
				FD_SET((*itr)->m_socket, &fset);
			}
		}
		m_clientlock.Release();
		select(0, &fset, NULL, NULL, NULL);
		if(FD_ISSET(m_listensocket, &fset))
		{
			RealmClient* clnt = new RealmClient();
			clnt->m_socket = WSAAccept(m_listensocket, NULL, NULL, NULL, 0);
			m_clientlock.Acquire();
			m_clients.push_back(clnt);
			m_clientlock.Release();
		}
		m_clientlock.Acquire();
		if(m_clients.size())
		{
			vector<RealmClient*>::iterator itr = m_clients.begin();
			vector<RealmClient*>::iterator end = m_clients.end();
			for( ; itr != end; ++itr)
			{
				if(FD_ISSET((*itr)->m_socket, &fset))
				{
					WSABUF wbuf;
					wbuf.buf = new char[65535];
					wbuf.len = 65535;
					DWORD bytes = 0, flags = 0;
					int x = WSARecv((*itr)->m_socket, &wbuf, 1, &bytes, &flags, NULL, NULL);
					if(x == SOCKET_ERROR)
					{
						RealmClient* clnt = (*itr);
						if(clnt->m_realm)
						{
							Log->Notice("CommHandler", "Closed realm '%s'", clnt->m_realm->name);
							m_realms.erase(clnt->m_realm);
							--m_realmid;
						}
						m_clients.erase(itr);
						end = m_clients.end();
						if(!m_clients.size())
							break;
						delete clnt;
					}
					RealmPacket* pk = new RealmPacket();
					pk->Resize(bytes);
					memcpy(pk->Contents(), wbuf.buf, bytes);
					(*itr)->HandlePacket(pk);
					delete [] wbuf.buf;
				}
			}
		}
		m_clientlock.Release();
	}
}

void RealmClient::HandlePacket(RealmPacket* pack)
{
	ui8 opcode;
	(*pack) >> opcode;
	switch(opcode)
	{
	case 0x00:
		{
			if(pack->GetSize() < 16)
			{
				Log->Notice("RealmClient", "Authentifizierung fehlgeschlagen!");
				closesocket(m_socket);
				break;
			}
			ui8 hash[16];
			pack->Read(hash, 16);
			if(sRealmHandler->CheckAuth(hash))
			{
				m_isauthed = true;
				RealmPacket pk = RealmPacket();
				pk << ui8(0x01) << ui8(0x00);
				send(m_socket, (char*)pk.Contents(), pk.GetSize(), 0);
				char* key = new char[2 * 16 + 1];
				for(int i = 0; i < 16; ++i)
					sprintf(key + 2 * i, "%02X", hash[i]);
				Log->Notice("RealmClient", "Authentifizierung erfolgreich, key '%s'", key);
				delete [] key;
			}
			else
			{
				RealmPacket pk;
				pk << ui8(0x01) << ui8(0x01);
				send(m_socket, (char*)pk.Contents(), pk.GetSize(), 0);
			}
			break;
		}
	case 0x02:
		{
			if(!m_isauthed)
			{
				RealmPacket pk;
				pk << ui8(0x03) << ui8(RAUTH_NOTAUTHED);
				send(m_socket, (char*)pk.Contents(), pk.GetSize(), 0);
				break;
			}
			m_realm = new Realm();
			m_realm->addr = new char[255];
			m_realm->name = new char[255];
			m_realm->id = sRealmHandler->GetNewId();
			(*pack) >> m_realm->name >> m_realm->addr >> m_realm->icon >> m_realm->color >> m_realm->population;
			sRealmHandler->m_realms.insert(m_realm);
			Log->Notice("RealmHandler", "Registered Realm '%s' (ID: %u)", m_realm->name, m_realm->id);
			RealmPacket pk;
			pk << ui8(0x03) << ui8(RAUTH_OK);
			send(m_socket, (char*)pk.Contents(), pk.GetSize(), 0);
			RealmPacket pk2;
			pk2 << ui8(0x12);
			send(m_socket, (char*)pk2.Contents(), pk.GetSize(), 0);
			break;
		}
	case 0x10:
		{
			char* accname = new char[50];
			ui32 requestid;
			(*pack) >> requestid >> accname;
			RealmPacket pack;
			pack << ui8(0x11) << requestid;
			if(!accname)
			{
				pack << ui8(1);
			}
			else
			{
				Account* acc = AccountMgr->GetAccount(accname);
				if(!acc)
					pack << ui8(1);
				else if(!acc->sessionkey)
					pack << ui8(1);
				else
				{
					pack << ui8(0);
					pack << ui32(acc->id);
					pack.Append(acc->sessionkey, 40);
				}
				pack << acc->gmstring;
				pack << (char*)acc->country.c_str();
			}
			send(m_socket, (char*)pack.Contents(), pack.GetSize(), 0);
			break;
		}
	case 0x13:
		{
			ui32 count;
			(*pack) >> count;
			for(++count ; count; --count)
			{
				ui32 accountid;
				ui8 chars;
				(*pack) >> accountid >> chars;
				if(m_realm)
					m_realm->mapping.insert(make_pair(accountid, chars));
			}
			break;
		}
	}
	delete pack;
}